import { ref, Ref, watch, defineComponent, computed, onMounted } from 'vue';
import {
  getDataPickerProps,
  getEditorEmits,
  renderString,
  useNamespace,
} from '@ibiz-template/vue3-util';
import { isNil, clone } from 'ramda';
import './ibiz-picker-dropdown.scss';
import { PickerEditorController } from '../picker-editor.controller';

export const IBizPickerDropDown = defineComponent({
  name: 'IBizPickerDropDown',
  props: getDataPickerProps<PickerEditorController>(),
  emits: getEditorEmits(),
  setup(props, { emit }) {
    const ns = useNamespace('picker-dropdown');

    const c = props.controller;

    // 当前值
    const curValue: Ref<string | null> = ref('');

    // 实体数据集
    const items: Ref<IData[]> = ref([]);

    // 是否能请求
    const shouldLoad = ref(false);

    // 获取关联数据项值
    const refValue = ref('');

    // 是否编辑态
    const isEditable = ref(false);

    // 编辑器Ref
    const editorRef = ref();

    // 加载中
    const loading: Ref<boolean> = ref(false);

    // 在搜索中时，再次触发搜索记录搜索值，等待上次搜索触发完成后再次搜索
    let waitQuery: string | null = null;

    // 是否已加载过
    const isLoaded = ref(false);

    // 是否显示表单默认内容
    const showFormDefaultContent = computed(() => {
      if (
        props.controlParams &&
        props.controlParams.editmode === 'hover' &&
        !props.readonly
      ) {
        return true;
      }
      return false;
    });

    watch(
      () => props.value,
      newVal => {
        if (newVal || newVal === null) {
          if (c.model.valueType === 'OBJECT') {
            curValue.value = newVal
              ? (newVal as IData)[c.objectNameField as string]
              : null;
          } else {
            curValue.value = newVal as string;
          }
          if (newVal === null) {
            curValue.value = '';
          }
          refValue.value = curValue.value as string;
          const value = props.data[c.valueItem];
          const index = items.value.findIndex((item: IData) =>
            Object.is(item[c.keyName], value),
          );
          if (index !== -1) {
            return;
          }
          // items里匹配不到当前值项值时，生成自身的选项
          items.value = [];
          if (!isNil(newVal) && !isNil(value)) {
            items.value.push({ [c.textName]: newVal, [c.keyName]: value });
          }
        }
      },
      { immediate: true },
    );

    // 搜索
    const onSearch = async (query: string) => {
      // 失焦那次不用发请求
      if (!shouldLoad.value) {
        return;
      }
      if (c.model.appDataEntityId && loading.value === false) {
        loading.value = true;
        try {
          let trimQuery = '';
          if (query !== props.value) {
            trimQuery = query.trim();
          }
          const res = await c.getServiceData(trimQuery, props.data!);
          if (res) {
            items.value = res.data as IData[];
          }
        } finally {
          loading.value = false;
          isLoaded.value = true;
          if (waitQuery != null) {
            const selfQuery = waitQuery;
            waitQuery = null;
            await onSearch(selfQuery);
          }
        }
      } else {
        waitQuery = query;
      }
    };

    onMounted(() => {
      watch(
        () => props.data[c.valueItem],
        async (newVal, oldVal) => {
          // 值项发生变化选中值项对应文本值
          if (newVal !== oldVal) {
            // 没有加载过先加载数据
            if (!isLoaded.value && editorRef.value && isNil(props.value)) {
              shouldLoad.value = true;
              await onSearch('');
            }
            refValue.value = newVal;
            // 如果值项被清空了，主文本也需清空
            if (newVal === null) {
              emit('change', null);
            }
          }
        },
        { immediate: true, deep: true },
      );
    });

    const setEditable = (flag: boolean) => {
      if (flag) {
        isEditable.value = flag;
      } else {
        setTimeout(() => {
          isEditable.value = flag;
        }, 100);
      }
    };

    // 往外抛值
    const onACSelect = async (data: IData) => {
      // 处理回填数据
      const dataItems = await c.calcFillDataItems(data);
      if (dataItems.length) {
        dataItems.forEach(dataItem => {
          emit('change', dataItem.value, dataItem.id);
        });
      }

      // 处理值项和本身的值
      const item: IData = clone(data);
      Object.assign(item, {
        [c.keyName]: item[c.keyName] ? item[c.keyName] : item.srfkey,
        [c.textName]: item[c.textName] ? item[c.textName] : item.srfmajortext,
      });
      if (c.valueItem) {
        emit('change', item[c.keyName], c.valueItem);
      }
      if (c.model.valueType === 'OBJECT') {
        emit('change', c.handleObjectParams(item));
      } else {
        emit('change', data[c.textName]);
      }
      setEditable(false);
    };

    // 值变更
    const onSelect = (select: string | Array<string>) => {
      const index = items.value.findIndex(item =>
        Object.is(item[c.keyName], select),
      );
      if (index >= 0) {
        onACSelect(items.value[index]);
      }
      shouldLoad.value = false;
    };

    // 下拉打开
    const onOpenChange = (isOpen: boolean) => {
      if (isOpen) {
        items.value = [];
        onSearch('');
      }
    };

    // 清除
    const onClear = () => {
      // 清空回填数据
      const dataItems = c.dataItems;
      if (dataItems?.length) {
        dataItems.forEach(dataItem => {
          emit('change', null, dataItem.id);
        });
      }
      if (c.valueItem) {
        emit('change', null, c.valueItem);
      }
      emit('change', null);
    };

    // 聚焦
    const onFocus = (e: IData) => {
      shouldLoad.value = true;
      emit('focus', e);
      setEditable(true);
    };

    // 失焦
    const onBlur = (e: IData) => {
      shouldLoad.value = false;
      emit('blur', e);
      setEditable(false);
    };

    // 处理点击键盘
    const handleKeyUp = (e: KeyboardEvent) => {
      if (e && e.code === 'Enter') {
        emit('enter', e);
      }
    };

    // 点击
    const onClick = () => {
      shouldLoad.value = true;
    };

    const valueText = computed(() => {
      return renderString(curValue.value);
    });

    watch(
      valueText,
      (newVal, oldVal) => {
        if (newVal !== oldVal) {
          emit('infoTextChange', newVal);
        }
      },
      { immediate: true },
    );

    return {
      ns,
      c,
      refValue,
      curValue,
      valueText,
      loading,
      items,
      onOpenChange,
      onClear,
      onSelect,
      onSearch,
      onFocus,
      onBlur,
      onClick,
      handleKeyUp,
      editorRef,
      isEditable,
      setEditable,
      showFormDefaultContent,
    };
  },
  render() {
    const overflowMode =
      this.c.editorParams.overflowMode || ibiz.config.pickerEditor.overflowMode;
    const isEllipsis = overflowMode === 'ellipsis';

    // 编辑态内容
    const editContent = this.readonly ? (
      this.value
    ) : (
      <el-select
        ref='editorRef'
        class={[this.ns.b('select')]}
        v-model={this.refValue}
        filterable
        remote
        remote-show-suffix
        clearable
        loading={this.loading}
        placeholder={this.c.placeHolder ? this.c.placeHolder : ' '}
        remote-method={this.onSearch}
        onVisibleChange={this.onOpenChange}
        onChange={this.onSelect}
        onClear={this.onClear}
        disabled={this.disabled}
        onFocus={this.onFocus}
        onBlur={this.onBlur}
        onKeyup={this.handleKeyUp}
        onClick={this.onClick}
        fit-input-width={isEllipsis}
        {...this.$attrs}
      >
        {this.items.map(item => {
          return (
            <el-option
              title={isEllipsis ? item[this.c.textName] : ''}
              value={item[this.c.keyName]}
              key={item[this.c.keyName]}
              label={item[this.c.textName]}
            ></el-option>
          );
        })}
      </el-select>
    );

    // 只读态内容
    const readonlyContent = (
      <div class={(this.ns.b(), this.ns.m('readonly'))}>{this.valueText}</div>
    );

    // 表单默认内容
    const formDefaultContent = (
      <div class={this.ns.b('form-default-content')}>
        {this.curValue ? this.valueText : '-'}
      </div>
    );

    return (
      <div
        class={[
          this.ns.b(),
          this.disabled ? this.ns.m('disabled') : '',
          this.readonly ? this.ns.m('readonly') : '',
          this.ns.is('editable', this.isEditable),
          this.ns.is('show-default', this.showFormDefaultContent),
        ]}
      >
        {this.showFormDefaultContent && formDefaultContent}
        {this.readonly ? readonlyContent : editContent}
      </div>
    );
  },
});
